#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<signal.h>
#include<arpa/inet.h>
#include<netdb.h>
#include<sys/wait.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
#include<pthread.h>
#include<sys/stat.h>

int m_MainId = 0;//Main ID
int m_ListenSocket = 0;//Listen Socket
char m_ConnectAddr[256] = {0};//
char m_ConnectPort[256] = {0};//

/*
** function name: GetListenSocket
** function func:generate listen socket
** input param: Port
** output param: no
*/
int GetListenSocket(int Port)
{
   struct sockaddr_in m_LisAddr = {0};
   int m_Socket = 0;
   int m_AddrLen = sizeof(struct sockaddr_in);

   //port information
   m_LisAddr.sin_family = AF_INET;
   m_LisAddr.sin_port = htons(Port);
   m_LisAddr.sin_addr.s_addr = INADDR_ANY;

   //create socket
   if((m_Socket = socket(AF_INET,SOCK_STREAM,0))<0)
   {
      //create socket error
      return 0;
   }
   
   // bind socket
   if(bind(m_Socket,(struct sockaddr *)&m_LisAddr,m_AddrLen)<0)
   {
      //bind socket error
      return 0;
   }

   //listen socket
   if(listen(m_Socket,5))
   {
      //listen socket error
      close(m_Socket);
      return 0;
   }
   
   //listen socket success
   return m_Socket;
}

/*
**  
*/
int GetConnectSocket(char *pServerAddr,char *pServerPort)
{
   struct sockaddr_in m_ServerAddr = {0};
   int m_AddrLen = sizeof(struct sockaddr_in);
   int m_Socket = 0;
  
   //init connect information
   m_ServerAddr.sin_addr.s_addr = inet_addr(pServerAddr);
   m_ServerAddr.sin_port = htons(atoi(pServerPort));
   m_ServerAddr.sin_family = AF_INET;

   //create send socket
   m_Socket = socket(AF_INET,SOCK_STREAM,0);
   if(m_Socket <= 0)
   {
      //failure
      return NULL;
   }

   //connect computer
   if(connect(m_Socket,(struct sockaddr *)&m_ServerAddr,m_AddrLen) < 0)
   {
      close(m_Socket);
      return NULL;
   }

   //connect success
   return m_Socket;
}

/*
**
*/
void TransSocket(int m_SendSocket,int m_RecvSocket)
{
   char m_Buf[512*1024] = {0};
   int ret = 0;
   fd_set readset;
   struct timeval tm = {0};
   tm.tv_sec = 3600*24;

   FD_ZERO(&readset);
   FD_SET(m_RecvSocket,&readset);

   while(1)
   {
      if((select(m_RecvSocket + 1, &readset, NULL, NULL ,&tm) <= 0))
      {
          //error
          break;
      }
      if(!FD_ISSET(m_RecvSocket,&readset))continue;

      ret = recv(m_RecvSocket,m_Buf,512 * 1024-1,0);
      if(ret < 0)
      {
          //error
          break;
      }
      send(m_SendSocket,m_Buf,ret,0);
   }
   close(m_SendSocket);
   close(m_RecvSocket);
}

/*
**
*/
void SocketTrans()
{
   struct sockaddr_in m_WorkAddr = {0};
   int m_191Socket = 0;
   int m_147socket = 0;
   int m_WorkAddrLen = 0;

   //start task exec
   while(1)
   {
      //accept 147's connect
      m_WorkAddrLen = sizeof(struct sockaddr_in);
      m_147socket = accept(m_ListenSocket,(struct sockaddr *)&m_WorkAddr,&m_WorkAddrLen);
      
      //check socket valid
      if(m_147socket < 0) continue;
 
      //connect 191
      m_191Socket = GetConnectSocket(m_ConnectAddr,m_ConnectPort);
      if(m_191Socket == NULL)
      {
         close(m_147socket);
         continue;
      }

      int ret = fork();
      if(ret <0)
      {
          //create new pro error
          printf("error,can't create new pro!\n");
          fflush(stdout);
          close(m_191Socket);
          close(m_147socket);
          break;
      }
      else if(ret == 0)
      {
          //close previous port
          close(m_ListenSocket);
         
          //create second pro, prevent from 
          ret = fork();
          if(ret <0)
          {
              close(m_191Socket);
              close(m_147socket);
              _exit(0);
          }
          else if(ret ==0)
          {
              //accept pro
              TransSocket(m_191Socket,m_147socket);
              _exit(0);
          }
          ret = fork();
          if(ret <0)
          {
              close(m_191Socket);
              close(m_147socket);
              _exit(0);
          }
          else if(ret ==0)
          {
              TransSocket(m_147socket,m_191Socket);
              _exit(0);
          }
          close(m_191Socket);
          close(m_147socket);
          _exit(0);
      }

      //wait sub-thread end
      close(m_191Socket);
      close(m_147socket);
      waitpid(ret,NULL,0);
  }
}

/*
**
*/
static void sig_usr(int signo)
{
   close(m_ListenSocket);
   if(m_MainId == getpid())
      kill(0,SIGKILL);
   exit(0);
}

static void sig_ign(int signo)
{
   fprintf(stderr,"signal %d catched, ignoring\n",signo);
}

int daemon_init()
{
   pid_t pid;
   if((pid=fork())<0){
      return -1;
   }else if(pid != 0){
      exit(0);
   }
   setsid();
   umask(0);
   return 0;
}

/*
**
*/
int main(int argc,char* argv[])
{
   //check param valid
   if(argc!=4)
   {
      printf("format:local port aim addr aim port\n");
      fflush(stdout);
      return 0;
   }

   daemon_init();
   
   //check-out signal event
   signal(SIGTERM,sig_usr);
   signal(SIGINT,sig_usr);
   signal(SIGQUIT,sig_usr);
   signal(SIGPIPE,sig_ign);
   signal(SIGALRM,sig_ign);
   signal(SIGQUIT,sig_ign);
   signal(SIGFPE,sig_ign);
   signal(SIGILL,sig_ign);
   signal(SIGPIPE,sig_ign);
   signal(SIGSEGV,sig_ign);
   signal(SIGTRAP,sig_ign);
   signal(SIGTSTP,sig_ign);

   //get parameters
   strcpy(m_ConnectAddr,argv[2]);
   strcpy(m_ConnectPort,argv[3]);

   //get listen socket
   m_ListenSocket = GetListenSocket(atoi(argv[1]));
   if(m_ListenSocket == 0)
   {
       printf("listen[%s]error!\n",argv[1]);
       fflush(stdout);
       return 0;
   }

   m_MainId = getpid();
   //start file listen thread
   SocketTrans();
   close(m_ListenSocket);
   return 0;
}
